package com.gw2.gw2world.web.server;

import java.io.StringReader;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import com.google.gson.stream.JsonReader;
import com.gw2.gw2world.web.server.model.Event;
import com.gw2.gw2world.web.server.model.Guild;
import com.gw2.gw2world.web.server.model.Item;
import com.gw2.gw2world.web.server.model.MiniMap;

/** Decode all JSON data
 * @author Seb
 *
 */
//@Repository("jsonDecoder")
//@Scope("singleton")
public class JsonDecoder {
	private static JsonDecoder instance;
	public static JsonDecoder getInstance() {
        if (null == instance) {
            instance = new JsonDecoder();
        }
        return instance;
    }
	private JsonDecoder() {}
	/** Decode and return game build
	 * @param json
	 * @return
	 */
	public int decodeBuild(String json) {
		int version=0;
		StringReader sr=new StringReader(json);
		try {
			JsonReader jr=new JsonReader(sr);
			jr.beginObject();
			while(jr.hasNext())
				if(jr.nextName().equals("build_id")) {version=jr.nextInt(); break;}
			//org.apache.log4j.Logger.getLogger("com.gw2.gw2world.web.server").info("version: "+String.valueOf(version));
			jr.close();
		} catch(Exception e) {
			org.apache.log4j.Logger.getLogger("com.gw2.gw2world.web.server").error("Unable to parse game build");
		} finally {
			sr.close();
		}
		return version;
	}
	/** Decode and return events and their status
	 * @param json
	 * @return
	 */
	public List<Event> decodeEvents(String json) {
		List<Event> result=new ArrayList<Event>();
		StringReader sr=new StringReader(json);
		String tag;
		try {
			JsonReader jr=new JsonReader(sr);
			jr.beginObject();
			if(jr.nextName().equals("events")) {
				jr.beginArray();
				while(jr.hasNext()) { //events:[]
					jr.beginObject();
					Event e=new Event();
					while(jr.hasNext()) {
						tag=jr.nextName();
						if(tag.equals("world_id")) e.setServer_id(jr.nextInt());
						else if(tag.equals("map_id")) e.setMap_id(jr.nextInt());
						else if(tag.equals("event_id")) e.setId(jr.nextString());
						else if(tag.equals("state")) e.setState(jr.nextString());
						else jr.skipValue();
					}
					jr.endObject();
					result.add(e);
				}
			}
			jr.close();
		} catch(Exception e) {
			org.apache.log4j.Logger.getLogger("com.gw2.gw2world.web.server").error("Unable to parse events");
		} finally {
			sr.close();
		}
		return result;
	}
	/** Decode event names
	 * @param json
	 * @return Result map: the key is the event_id and the value is the event name
	 */
	public Map<String,String> decodeEventNames(String json) {
		Map<String,String> result=new HashMap<String, String>();
		StringReader sr=new StringReader(json);
		String id,name,tag;
		try {
			JsonReader jr=new JsonReader(sr);
			jr.beginArray();
			while(jr.hasNext()) {
				jr.beginObject();
				id=""; name="";
				while(jr.hasNext()) {
					tag=jr.nextName();
					if(tag.equals("id")) id=jr.nextString();
					else if(tag.equals("name")) name=jr.nextString();
					else jr.skipValue();
				}
				jr.endObject();
				result.put(id, name);
			}
			jr.close();
		} catch(Exception e) {
			org.apache.log4j.Logger.getLogger("com.gw2.gw2world.web.server").error("Unable to parse event names");
		} finally {
			sr.close();
		}
		return result;
	}
	/** Decode map names
	 * @param json
	 * @return Result map: the key is the map_id and the value is the map name
	 */
	public Map<Integer,String> decodeMapNames(String json) {
		Map<Integer,String> result=new HashMap<Integer, String>();
		StringReader sr=new StringReader(json);
		int id;
		String name,tag;
		try {
			JsonReader jr=new JsonReader(sr);
			jr.beginArray();
			while(jr.hasNext()) {
				jr.beginObject();
				id=0; name="";
				while(jr.hasNext()) {
					tag=jr.nextName();
					if(tag.equals("id")) id=Integer.parseInt(jr.nextString());
					else if(tag.equals("name")) name=jr.nextString();
					else jr.skipValue();
				}
				jr.endObject();
				result.put(id, name);
			}
			jr.close();
		} catch(Exception e) {
			org.apache.log4j.Logger.getLogger("com.gw2.gw2world.web.server").error("Unable to parse map names");
		} finally {
			sr.close();
		}
		return result;
	}
	/** Decode server names
	 * @param json
	 * @return Result map: the key is the server_id and the value is the server name
	 */
	public Map<Integer,String> decodeServerNames(String json) {
		Map<Integer,String> result=new HashMap<Integer, String>();
		StringReader sr=new StringReader(json);
		int id;
		String name, tag;
		try {
			JsonReader jr=new JsonReader(sr);
			jr.beginArray();
			while(jr.hasNext()) {
				jr.beginObject();
				id=0; name="";
				while(jr.hasNext()) {
					tag=jr.nextName();
					if(tag.equals("id")) id=Integer.parseInt(jr.nextString());
					else if(tag.equals("name")) name=jr.nextString();
					else jr.skipValue();
				}
				jr.endObject();
				result.put(id, name);
			}
			jr.close();
		} catch(Exception e) {
			org.apache.log4j.Logger.getLogger("com.gw2.gw2world.web.server").error("Unable to parse server names");
		} finally {
			sr.close();
		}
		return result;
	}
	public List<MiniMap> decodeMapDetails(String json) {
		List<MiniMap> maps=new ArrayList<MiniMap>();
		MiniMap mm;
		try {
			StringReader sr=new StringReader(json);
			JsonReader jr=new JsonReader(sr);
			try {
				jr.beginObject();
				String tag;
				while(jr.hasNext()) {
					tag=jr.nextName();
					if(tag.equals("maps")) {
						jr.beginObject();
						int id=0;
						while(jr.hasNext()) {
							tag=jr.nextName(); //map ID
							try {
								mm=new MiniMap();
								id=Integer.parseInt(tag);
								mm.setId(id);
								jr.beginObject();
								while(jr.hasNext()) {
									tag=jr.nextName();
									if(tag.equals("min_level")) mm.setMin_level(jr.nextInt());
									else if(tag.equals("max_level")) mm.setMax_level(jr.nextInt());
									else if(tag.equals("region_id")) mm.setRegion_id(jr.nextInt());
									else if(tag.equals("region_name")) mm.setRegion_name(jr.nextString());
									else if(tag.equals("continent_id")) mm.setContinent_id(jr.nextInt());
									else if(tag.equals("continent_name")) mm.setContinent_name(jr.nextString());
									else jr.skipValue();
								}
								jr.endObject();
								maps.add(mm);
							} catch(Exception e) {
								jr.skipValue();
							}
						}
						jr.endObject();
					}
					else jr.skipValue();
				}
			} finally {
				jr.close();
				sr.close();
			}
		} catch(Exception e) {
			org.apache.log4j.Logger.getLogger("com.gw2.gw2world.web.server").error("Unable to parse map details");
		}
		return maps;
	}
	/** Get event details
	 * @param json
	 */
	public List<Event> decodeEventDetails(String json) {
		//FIXME: somewhere: getting same elements 
		List<Event> result=new ArrayList<Event>();
		String tag="";
		//int n=0;
		try {
			StringReader sr=new StringReader(json);
			JsonReader jr=new JsonReader(sr);
			Event e;
			try {
				jr.beginObject(); //open root object
				while(jr.hasNext()) {
					tag=jr.nextName();
					if(tag.equals("events")) { //events object
						jr.beginObject();
						while(jr.hasNext()) {//event detail
							e=new Event();
							tag=jr.nextName();
							//System.out.println(tag+" "+String.valueOf(++n));
							e.setId(tag);
							jr.beginObject();
							while(jr.hasNext()) {
								tag=jr.nextName();
								if(tag.equals("level")) e.setLevel(jr.nextInt());
								else if(tag.equals("map_id")) e.setMap_id(jr.nextInt());
								else if(tag.equals("flags")) {
									jr.beginArray();
									while(jr.hasNext()) {
										tag=jr.nextString();
										if(tag.equals("group_event")) e.setRequireGroup(true);
										else if(tag.equals("map_wide"));
										else jr.skipValue();
									}
									jr.endArray();
								} //else if(tag.equals("location")) {}
								else jr.skipValue();
							}
							jr.endObject();
							result.add(e);
						}
						jr.endObject();
					}
					else jr.skipValue();
				}
			} finally {
				jr.close();
				sr.close();
			}
		} catch(Exception e) {
			org.apache.log4j.Logger.getLogger("com.gw2.gw2world.web.server").error("Unable to parse event details");
		}
		return result;
	}
	
	/** Get guild information. If the guild is not found, it returns null
	 * @param json
	 * @return Guild object or null if not found
	 */
	public Guild decodeGuild(String json) {
		if(json.contains("\"error\"")) //{"error":3019,"product":1008,"module":1,"line":561,"text":null}
			return null;
		Guild g = new Guild();
		String tag;
		try {
			StringReader sr = new StringReader(json);
			JsonReader jr = new JsonReader(sr);
			try {
				jr.beginObject();
				while(jr.hasNext()) { //get guild info
					tag=jr.nextName();
					if(tag.equals("guild_id")) g.setId(jr.nextString());
					else if(tag.equals("guild_name")) g.setName(jr.nextString());
					else if(tag.equals("tag")) g.setTag(jr.nextString());
					else if(tag.equals("emblem")) { //get emblem info
						jr.beginObject();
						while(jr.hasNext()) {
							tag=jr.nextName();
							if(tag.equals("background_id")) g.setBackground_id(jr.nextInt());
							else if(tag.equals("foregrund_id ")) g.setForeground_id(jr.nextInt());
							else if(tag.equals("background_color_id ")) g.setBackground_color_id(jr.nextInt());
							else if(tag.equals("foreground_primary_color_id ")) g.setForeground_primary_color_id(jr.nextInt());
							else if(tag.equals("foreground_seconday_color_id ")) g.setForeground_seconday_color_id(jr.nextInt());
							else jr.skipValue();
						}
						jr.endObject();
					} else jr.skipValue();
				}
			} finally {
				jr.close();
				sr.close();
			}
		} catch (Exception e) {
			e.printStackTrace();
			return null;
		}
		return g;
	}
	/** Get the list of all item IDs or all recipe IDs: It is the same JSON structure, just the object name is changing.
	 * @param json
	 * @return
	 */
	public List<Integer> getItemIdsOrRecipeIds(String json) {
		List<Integer> result=new ArrayList<Integer>();
		try {
			StringReader sr = new StringReader(json);
			JsonReader jr = new JsonReader(sr);
			try {
				jr.beginObject();
				String tag=jr.nextName();
				if(tag.equals("items") || tag.equals("recipes")) {
					jr.beginArray();
					while(jr.hasNext()) {
						result.add(jr.nextInt());
					}
				}
			} finally {
				jr.close();
				sr.close();
			}
		} catch (Exception e) {
			org.apache.log4j.Logger.getLogger("com.gw2.gw2world.web.server").error("Unable to parse item IDs");
		}
		return result;
	}
	public void getRecipeDetail(String json) {
		
	}
	//decode? https://api.guildwars2.com/v1/map_floor.json?continent_id=1&floor=1&lang=xx
	public Item getItemDetail(String json) {
		Item i=new Item();
		try {
			StringReader sr = new StringReader(json);
			JsonReader jr = new JsonReader(sr);
			try {
				jr.beginObject();
				String tag;
				while(jr.hasNext()) {
					tag=jr.nextName();
					if(tag.equals("item_id")) i.setId(Integer.parseInt(jr.nextString()));
					else if(tag.equals("rarity")) i.setRarity(jr.nextString());
					else if(tag.equals("vendor_value")) i.setVendor_value(Integer.parseInt(jr.nextString()));
					else if(tag.equals("icon_file_id")) i.setIcon_file_id(Integer.parseInt(jr.nextString()));
					else if(tag.equals("icon_file_signature")) i.setIcon_file_signature(jr.nextString());
					else jr.skipValue();
				}
			} finally {
				jr.close();
				sr.close();
			}
		} catch (Exception e) {
			org.apache.log4j.Logger.getLogger("com.gw2.gw2world.web.server").error("Unable to parse item");
			return null;
		}
		return i;
	}
}
